<!DOCTYPE html>
<html>
    <head>
        <link rel='stylesheet' type='text/css' href='web.css'>
        <script src='../../dist-web/externals.min.js'></script>
        <script src='../../dist-web/eosjs-api.min.js'></script>
        <script src='../../dist-web/eosjs-jsonrpc.min.js'></script>
        <script src='../../dist-web/eosjs-jssig.min.js'></script>
        <script>
            const privateKey = '5JuH9fCXmU3xbj8nRmhPZaVrxxXrdPaRmZLW1cznNTmTQR2Kg5Z'; // replace with 'bob' account private key
            const r1PrivateKey = 'PVT_R1_GrfEfbv5at9kbeHcGagQmvbFLdm6jqEpgE1wsGbrfbZNjpVgT'
            const cfactorPrivateKey = '5K8Sm2bB2b7ZC8tJMefrk1GFa4jgtHxxHRcjX49maMk9AEwq8hN';
            /* new accounts for testing can be created by unlocking a cleos wallet then calling: 
            * 1) cleos create key --to-console (copy this privateKey & publicKey)
            * 2) cleos wallet import 
            * 3) cleos create account bob publicKey
            * 4) cleos create account alice publicKey
            */
        
            const rpc = new eosjs_jsonrpc.JsonRpc('http://localhost:8888');
            const signatureProvider = new eosjs_jssig.JsSignatureProvider([privateKey, r1PrivateKey, cfactorPrivateKey]);
            const api = new eosjs_api.Api({ rpc, signatureProvider });
        
            const EXECUTING = 'Executing Test', SUCCESS = 'Success', FAILED = 'Failed';
            let resultsLabel, transactionResponse, transactionSignatures, failedAsPlanned;

            const transactWithConfig = async (config, memo, from = 'bob', to = 'alice') => {
              return api.transact({
                actions: [{
                  account: 'eosio.token',
                  name: 'transfer',
                  authorization: [{
                    actor: from,
                    permission: 'active',
                  }],
                  data: {
                    from,
                    to,
                    quantity: '0.0001 SYS',
                    memo,
                  },
                }]
              }, config);
            }
        
            const testTransactWithConfigBlocksBehind = async (e) => {
                resultsLabel = e.target;
                resultsLabel.innerText = EXECUTING;
        
                try {
                    transactionResponse = await transactWithConfig({ blocksBehind: 3, expireSeconds: 30 }, 'transactWithBlocksBehind');
                } catch (error) {
                    resultsLabel.className = 'failed';
                    resultsLabel.innerText = FAILED;
                    console.error('Transact With Config Blocks Behind Test Failure: ', error.message);
                    return false;
                }
        
                if (transactionResponse.transaction_id) {
                    resultsLabel.className = "success";
                    resultsLabel.innerText = SUCCESS;
                    return true;
                }
                resultsLabel.className = 'failed';
                resultsLabel.innerText = FAILED;
                return false;
            }

            const testTransactWithConfigUseLastIrreversible = async (e) => {
              resultsLabel = e.target;
              resultsLabel.innerText = EXECUTING;

              try {
                transactionResponse = await transactWithConfig({ useLastIrreversible: true, expireSeconds: 30 }, 'transactWithUseLastIrreversible');
              } catch (error) {
                resultsLabel.className = 'failed';
                resultsLabel.innerText = FAILED;
                console.error('Transact With Config Use Last Irreversible Test Failure: ', error.message);
                return false;
              }

              if (transactionResponse.transaction_id) {
                resultsLabel.className = "success";
                resultsLabel.innerText = SUCCESS;
                return true;
              }
              resultsLabel.className = 'failed';
              resultsLabel.innerText = FAILED;
              return false;
            }
        
            const transactWithoutConfig = async () => {
                const transactionResponse = await transactWithConfig({ blocksBehind: 3, expireSeconds: 30 }, 'transactWithoutConfig');
                const blockInfo = await rpc.get_block_info(transactionResponse.processed.block_num - 3);
                const currentDate = new Date();
                const timePlusTen = currentDate.getTime() + 10000;
                const timeInISOString = (new Date(timePlusTen)).toISOString();
                const expiration = timeInISOString.substr(0, timeInISOString.length - 1);
        
                return await api.transact({
                    expiration,
                    ref_block_num: blockInfo.block_num & 0xffff,
                    ref_block_prefix: blockInfo.ref_block_prefix,
                    actions: [{
                        account: 'eosio.token',
                        name: 'transfer',
                        authorization: [{
                            actor: 'bob',
                            permission: 'active',
                        }],
                        data: {
                            from: 'bob',
                            to: 'alice',
                            quantity: '0.0001 SYS',
                            memo: 'transactWithoutConfig2',
                        },
                    }]
                });
            };
        
            const testTransactWithoutConfig = async (e) => {
                resultsLabel = e.target;
                resultsLabel.innerText = EXECUTING;
        
                try {
                    transactionResponse = await transactWithoutConfig();
                } catch (error) {
                    resultsLabel.className = 'failed';
                    resultsLabel.innerText = FAILED;
                    console.error('Transact without Config Test Failure: ', error.message);
                    return false;
                }
        
                if (transactionResponse.transaction_id) {
                    resultsLabel.className = "success";
                    resultsLabel.innerText = SUCCESS;
                    return true;
                }
                resultsLabel.className = 'failed';
                resultsLabel.innerText = FAILED;
                return false;
            };

            const testTransactWithCompression = async (e) => {
              resultsLabel = e.target;
              resultsLabel.innerText = EXECUTING;

              try {
                transactionResponse =
                  await transactWithConfig({ blocksBehind: 3, expireSeconds: 30, compression: true }, 'transactWithCompression');
              } catch (error) {
                resultsLabel.className = 'failed';
                resultsLabel.innerText = FAILED;
                console.error('Transact With Config Test Failure: ', error.message);
                return false;
              }

              if (transactionResponse.transaction_id) {
                resultsLabel.className = "success";
                resultsLabel.innerText = SUCCESS;
                return true;
              }
              resultsLabel.className = 'failed';
              resultsLabel.innerText = FAILED;
              return false;
            }

            const transactWithContextFree = async (user, context_free_data) => {
              return api.transact({
                actions: [{
                  account: 'cfhello',
                  name: 'normal',
                  authorization: [{
                    actor: 'cfactor',
                    permission: 'active'
                  }],
                  data: {
                    user
                  }
                }],
                context_free_actions: [{
                  account: 'cfhello',
                  name: 'contextfree',
                  authorization: [],
                  data: {}
                }],
                context_free_data
              }, {
                blocksBehind: 3,
                expireSeconds: 30
              });
            }

            const testTransactWithContextFreeAction = async (e) => {
              resultsLabel = e.target;
              resultsLabel.innerText = EXECUTING;

              try {
                transactionResponse =
                  await transactWithContextFree('test', []);
              } catch (error) {
                resultsLabel.className = 'failed';
                resultsLabel.innerText = FAILED;
                console.error('Transact With Context Free Action Test Failure: ', error.message);
                return false;
              }

              if (transactionResponse.transaction_id) {
                resultsLabel.className = "success";
                resultsLabel.innerText = SUCCESS;
                return true;
              }
              resultsLabel.className = 'failed';
              resultsLabel.innerText = FAILED;
              return false;
            }

            const testTransactWithContextFreeData = async (e) => {
              resultsLabel = e.target;
              resultsLabel.innerText = EXECUTING;

              try {
                transactionResponse =
                  await transactWithContextFree('test2', [[ '74657374', '7465737464617461' ]]);
              } catch (error) {
                resultsLabel.className = 'failed';
                resultsLabel.innerText = FAILED;
                console.error('Transact With Context Free Data Test Failure: ', error.message);
                return false;
              }

              if (transactionResponse.transaction_id) {
                resultsLabel.className = "success";
                resultsLabel.innerText = SUCCESS;
                return true;
              }
              resultsLabel.className = 'failed';
              resultsLabel.innerText = FAILED;
              return false;
            }
        
            const testTransactWithoutBroadcast = async (e) => {
                resultsLabel = e.target;
                resultsLabel.innerText = EXECUTING;
        
                try {
                    transactionSignatures =
                      await transactWithConfig({ broadcast: false, blocksBehind: 3, expireSeconds: 30 }, 'transactWithoutBroadcast');
                } catch (error) {
                    resultsLabel.className = 'failed';
                    resultsLabel.innerText = FAILED;
                    console.error('Transact without Broadcast Test Failure: ', error.message);
                    return false;
                }
        
                if(transactionSignatures.signatures && transactionSignatures.serializedTransaction) {
                    resultsLabel.className = "success";
                    resultsLabel.innerText = SUCCESS;
                    return true;
                }
                resultsLabel.className = 'failed';
                resultsLabel.innerText = FAILED;
                return false;
            };
        
            const broadcastResult = async (signaturesAndPackedTransaction) => await api.pushSignedTransaction(signaturesAndPackedTransaction);
        
            const testBroadcastResult = async (e) => {
                resultsLabel = e.target;
                resultsLabel.innerText = EXECUTING;
        
                try {
                    transactionSignatures =
                      await transactWithConfig({ broadcast: false, blocksBehind: 3, expireSeconds: 30 }, 'transactWithoutBroadcast2');
                    transactionResponse = await broadcastResult(transactionSignatures);
                } catch (error) {
                    resultsLabel.className = 'failed';
                    resultsLabel.innerText = FAILED;
                    console.error('Broadcast Transaction Test Failure: ', error.message);
                    return false;
                }
        
                if (transactionResponse.transaction_id) {
                    resultsLabel.className = "success";
                    resultsLabel.innerText = SUCCESS;
                    return true;
                }
                resultsLabel.className = 'failed';
                resultsLabel.innerText = FAILED;
                return false;
            }

            const shorthandWithApiJson = async () => {
              await api.getAbi('eosio.token');
              return api.transact({
                actions: [
                  api.with('eosio.token').as('bob').transfer('bob', 'alice', '0.0001 SYS', 'transactWithShorthandApiJson')
                ]
              }, {
                blocksBehind: 3,
                expireSeconds: 30
              });
            }

            const testShorthandWithApiJson = async (e) => {
              resultsLabel = e.target;
              resultsLabel.innerText = EXECUTING;

              try {
                transactionResponse = await shorthandWithApiJson();
              } catch (error) {
                resultsLabel.className = 'failed';
                resultsLabel.innerText = FAILED;
                console.error('Transact with .with() Using Api and Json Abi Test Failure: ', error.message);
                return false;
              }

              if (transactionResponse.transaction_id) {
                resultsLabel.className = "success";
                resultsLabel.innerText = SUCCESS;
                return true;
              }
              resultsLabel.className = 'failed';
              resultsLabel.innerText = FAILED;
              return false;
            };

            const shorthandWithTxJson = async () => {
              await api.getAbi('eosio.token');
              const tx = api.buildTransaction();
              tx.with('eosio.token').as('bob').transfer('bob', 'alice', '0.0001 SYS', 'transactWithShorthandTxJson');
              return tx.send({
                blocksBehind: 3,
                expireSeconds: 30
              });
            }

            const testShorthandWithTxJson = async (e) => {
              resultsLabel = e.target;
              resultsLabel.innerText = EXECUTING;

              try {
                transactionResponse = await shorthandWithTxJson();
              } catch (error) {
                resultsLabel.className = 'failed';
                resultsLabel.innerText = FAILED;
                console.error('Transact with .with() Using Tx and Json Abi Test Failure: ', error.message);
                return false;
              }

              if (transactionResponse.transaction_id) {
                resultsLabel.className = "success";
                resultsLabel.innerText = SUCCESS;
                return true;
              }
              resultsLabel.className = 'failed';
              resultsLabel.innerText = FAILED;
              return false;
            };

            const shorthandWithTxJsonContextFree = async (user, context_free_data) => {
              await api.getAbi('cfhello');
              const tx = api.buildTransaction();
              tx.associateContextFree(() => ({
                contextFreeAction: tx.with('cfhello').as().contextfree(),
                action: tx.with('cfhello').as('cfactor').normal(user),
                context_free_data
              }));
              return await tx.send({
                blocksBehind: 3,
                expireSeconds: 30
              });
            }

            const testShorthandWithTxJsonContextFreeAction = async (e) => {
              resultsLabel = e.target;
              resultsLabel.innerText = EXECUTING;

              try {
                transactionResponse = await shorthandWithTxJsonContextFree('test');
              } catch (error) {
                resultsLabel.className = 'failed';
                resultsLabel.innerText = FAILED;
                console.error('Transact with .with() Using Tx and Json Abi with Context Free Action: ', error.message);
                return false;
              }

              if (transactionResponse.transaction_id) {
                resultsLabel.className = "success";
                resultsLabel.innerText = SUCCESS;
                return true;
              }
              resultsLabel.className = 'failed';
              resultsLabel.innerText = FAILED;
              return false;
            }

            const testShorthandWithTxJsonContextFreeData = async (e) => {
              resultsLabel = e.target;
              resultsLabel.innerText = EXECUTING;

              try {
                transactionResponse = await shorthandWithTxJsonContextFree('test2', [ '74657374', '7465737464617461' ]);
              } catch (error) {
                resultsLabel.className = 'failed';
                resultsLabel.innerText = FAILED;
                console.error('Transact with .with() Using Tx and Json Abi with Context Free Data: ', error.message);
                return false;
              }

              if (transactionResponse.transaction_id) {
                resultsLabel.className = "success";
                resultsLabel.innerText = SUCCESS;
                return true;
              }
              resultsLabel.className = 'failed';
              resultsLabel.innerText = FAILED;
              return false;
            }

            const testWithP256EllipticCurve = async (e) => {
              resultsLabel = e.target;
              resultsLabel.innerText = EXECUTING;

              try {
                transactionResponse = await transactWithConfig({ blocksBehind: 3, expireSeconds: 30 }, 'transactWithR1KeySignature', 'bobr1', 'alicer1');
              } catch (error) {
                resultsLabel.className = 'failed';
                resultsLabel.innerText = FAILED;
                console.error('Transact elliptic p256/KeyType.r1 Keys and Signatures Test Failure: ', error.message);
                return false;
              }

              if (transactionResponse.transaction_id) {
                resultsLabel.className = "success";
                resultsLabel.innerText = SUCCESS;
                return true;
              }
              resultsLabel.className = 'failed';
              resultsLabel.innerText = FAILED;
              return false;
            };

            const returnValueTx = async () => {
              await api.getAbi('returnvalue');
              const tx = api.buildTransaction();
              tx.with('returnvalue').as('bob').sum(5, 5);
              return tx.send({
                  blocksBehind: 3,
                  expireSeconds: 30
              });
            }

            const testWithReturnValueTx = async (e) => {
              const expectedValue = 10;
              resultsLabel = e.target;
              resultsLabel.innerText = EXECUTING;

              try {
                transactionResponse = await returnValueTx();
              } catch (error) {
                resultsLabel.className = 'failed';
                resultsLabel.innerText = FAILED;
                console.error('Transact Return Values Test Failure: ', error.message);
                return false;
              }

              if (transactionResponse.processed.action_traces[0].return_value_data === expectedValue) {
                resultsLabel.className = "success";
                resultsLabel.innerText = SUCCESS;
                return true;
              }
              resultsLabel.className = 'failed';
              resultsLabel.innerText = FAILED;
              return false;
            }

            const resourcePayerTx = async () => {
              return await api.transact({
                resource_payer: {
                    payer: 'alice',
                    max_net_bytes: 4096,
                    max_cpu_us: 400,
                    max_memory_bytes: 0
                },
                actions: [{
                    account: 'eosio.token',
                    name: 'transfer',
                    authorization: [{
                        actor: 'bob',
                        permission: 'active',
                    }, {
                        actor: 'alice',
                        permission: 'active',
                    }],
                    data: {
                        from: 'bob',
                        to: 'alice',
                        quantity: '0.0001 SYS',
                        memo: 'resource payer',
                    },
                }]
              }, {
                  blocksBehind: 3,
                  expireSeconds: 30
              });

            }

            const testWithResourcePayerTx = async (e) => {
              resultsLabel = e.target;
              resultsLabel.innerText = EXECUTING;

              try {
                transactionResponse = await resourcePayerTx();
              } catch (error) {
                resultsLabel.className = 'failed';
                resultsLabel.innerText = FAILED;
                console.error('Transact Resource Payer Test Failure: ', error.message);
                return false;
              }

              if (transactionResponse.transaction_id) {
                resultsLabel.className = "success";
                resultsLabel.innerText = SUCCESS;
                return true;
              }
              resultsLabel.className = 'failed';
              resultsLabel.innerText = FAILED;
              return false;
            }

            const readOnlyQuery = async () => {
              return await api.transact({
                  actions: [{
                      account: 'readonly',
                      name: 'get',
                      authorization: [{
                          actor: 'readonly',
                          permission: 'active',
                      }],
                      data: {},
                  }],
              }, {
                  blocksBehind: 3,
                  expireSeconds: 30,
                  compression: true,
                  readOnlyTrx: true,
              });
            }

            const testWithReadOnlyQuery = async (e) => {
              const expectedValue = [
                {'id': 1, 'name': 'Bob Smith', 'gender': 1, 'age': 25},
                {'id': 3, 'name': 'John Smith', 'gender': 1, 'age': 42},
                {'id': 4, 'name': 'Jack Smith', 'gender': 1, 'age': 27},
                {'id': 2, 'name': 'Alice Smith', 'gender': 0, 'age': 20,},
                {'id': 5, 'name': 'Youko Niihara', 'gender': 0, 'age': 26},
                {'id': 6, 'name': 'Rose Lee', 'gender': 0, 'age': 18},
                {'id': 7, 'name': 'Youko Kawakami', 'gender': 0, 'age': 25},
                {'id': 8, 'name': 'Yuu Yamada', 'gender': 0, 'age': 24}
              ];
              resultsLabel = e.target;
              resultsLabel.innerText = EXECUTING;

              try {
                transactionResponse = await readOnlyQuery();
              } catch (error) {
                resultsLabel.className = 'failed';
                resultsLabel.innerText = FAILED;
                console.error('Transact Read Only Query Test Failure: ', error.message);
                return false;
              }

              if (JSON.stringify(transactionResponse.result.action_traces[0].return_value_data) === JSON.stringify(expectedValue)) {
                resultsLabel.className = "success";
                resultsLabel.innerText = SUCCESS;
                return true;
              }
              resultsLabel.className = 'failed';
              resultsLabel.innerText = FAILED;
              return false;
            }

            const readOnlyFailureTrace = async () => {
              return await api.transact({
                actions: [{
                    account: 'eosio',
                    name: 'setpriv',
                    authorization: [{
                        actor: 'bob',
                        permission: 'active',
                    }],
                    data: {
                        account: 'bob',
                        is_priv: '1'
                    },
                }]
              }, {
                  blocksBehind: 3,
                  expireSeconds: 30,
                  readOnlyTrx: true,
                  returnFailureTraces: true,
              });
            }

            const testWithReadOnlyFailureTrace = async (e) => {
              let err;
              resultsLabel = e.target;
              resultsLabel.innerText = EXECUTING;
              
              try {
                await readOnlyFailureTrace();
              } catch (err) {
                if (err.details.code === 3090004 && err.details.stack[0].format === 'missing authority of ${account}') {
                    resultsLabel.className = "success";
                    resultsLabel.innerText = SUCCESS;
                    return true;
                }
              }
              resultsLabel.className = 'failed';
              resultsLabel.innerText = FAILED;
              return false;
            }
        
            const transactShouldFail = async () => await api.transact({
                actions: [{
                    account: 'eosio.token',
                    name: 'transfer',
                    authorization: [{
                        actor: 'bob',
                        permission: 'active',
                    }],
                    data: {
                        from: 'bob',
                        to: 'alice',
                        quantity: '0.0001 SYS',
                        memo: '',
                    },
                }]
            });
        
            const testTransactShouldFail = async (e) => {
                resultsLabel = e.target;
                resultsLabel.innerText = EXECUTING;
        
                try {
                    await transactShouldFail();
                } catch (e) {
                    if (e.message === 'Required configuration or TAPOS fields are not present') {
                        resultsLabel.className = "success";
                        resultsLabel.innerText = SUCCESS;
                        return true;
                    }
                }
                resultsLabel.className = 'failed';
                resultsLabel.innerText = FAILED;
                return false;
            }
            
            const rpcShouldFail = async () => await rpc.get_block_info(-1);
        
            const testRpcShouldFail = async (e) => {
                resultsLabel = e.target;
                resultsLabel.innerText = EXECUTING;
        
                try {
                    await rpcShouldFail();
                } catch (e) {
                    if (e instanceof eosjs_jsonrpc.RpcError) {
                        resultsLabel.className = "success";
                        resultsLabel.innerText = SUCCESS;
                        return true;
                    }
                }
                resultsLabel.className = 'failed';
                resultsLabel.innerText = FAILED;
                return false;
            }
        
            const runAllTests = async () => {
                const buttons = document.getElementsByTagName('button');
                for (var i = 1; i < buttons.length; i++) {
                    var button = buttons[i];
                    button.click();
                    await new Promise(resolve => setTimeout(resolve, 150))
                }
                return;
            }
        </script>
    </head>
    <body>
        <div class='header-container'>
            <div class='header'>
                <img src='logo.svg'/>
                <h1>Web Build Integration Tests</h1>
                <button onClick='runAllTests();'>Run All Tests</button>
            </div>
        </div>
        <div class='tests'>
            <div><h2>Transact with blocksBehind Configuration Parameter</h2><button id='testTransactWithConfigBlocksBehind' onClick='testTransactWithConfigBlocksBehind(event);'>Test</button></div>
            <div><h2>Transact with useLastIrreversible Configuration Parameter</h2><button id='testTransactWithConfigUseLastIrreversible' onClick='testTransactWithConfigUseLastIrreversible(event);'>Test</button></div>
            <div><h2>Transact with Manually Configured TAPOS</h2><button id='testTransactWithoutConfig' onClick='testTransactWithoutConfig(event);'>Test</button></div>
            <div><h2>Transact with Compression</h2><button id='testTransactWithCompression' onClick='testTransactWithCompression(event);'>Test</button></div>
            <div><h2>Transact with Context Free Action</h2><button id='testTransactWithContextFreeAction' onClick='testTransactWithContextFreeAction(event);'>Test</button></div>
            <div><h2>Transact with Context Free Data</h2><button id='testTransactWithContextFreeData' onClick='testTransactWithContextFreeData(event);'>Test</button></div>
            <div><h2>Transact without Broadcasting</h2><button id='testTransactWithoutBroadcast' onClick='testTransactWithoutBroadcast(event);'>Test</button></div>
            <div><h2>Broadcast Transaction</h2><button id='testBroadcastResult' onClick='testBroadcastResult(event);'>Test</button></div>
            <div><h2>Transact with .with() Using Api and Json Abi</h2><button id='testShorthandWithApiJson' onClick='testShorthandWithApiJson(event);'>Test</button></div>
            <div><h2>Transact with .with() Using Tx and Json Abi</h2><button id='testShorthandWithTxJson' onClick='testShorthandWithTxJson(event);'>Test</button></div>
            <div><h2>Transact with .with() Using Tx and Json Abi with Context Free Action</h2><button id='testShorthandWithTxJsonContextFreeAction' onClick='testShorthandWithTxJsonContextFreeAction(event);'>Test</button></div>
            <div><h2>Transact with .with() Using Tx and Json Abi with Context Free Data</h2><button id='testShorthandWithTxJsonContextFreeData' onClick='testShorthandWithTxJsonContextFreeData(event);'>Test</button></div>
            <div><h2>Transact elliptic p256/KeyType.r1 Keys and Signatures</h2><button id='testWithP256EllipticCurve' onClick='testWithP256EllipticCurve(event);'>Test</button></div>
            <div><h2>Transact Return Values</h2><button id='testWithReturnValueTx' onClick='testWithReturnValueTx(event);'>Test</button></div>
            <div><h2>Resource Payer</h2><button id='testWithResourcePayerTx' onClick='testWithResourcePayerTx(event);'>Test</button></div> 
            <div><h2>Read Only Query</h2><button id='testWithReadOnlyQuery' onClick='testWithReadOnlyQuery(event);'>Test</button></div>
            <div><h2>Read Only Failure Trace</h2><button id='testWithReadOnlyFailureTrace' onClick='testWithReadOnlyFailureTrace(event);'>Test</button></div>
            <div><h2>Invalid Transaction Throws Error</h2><button id='testTransactShouldFail' onClick='testTransactShouldFail(event);'>Test</button></div>
            <div><h2>Invalid Rpc Call Throws Rpc Error</h2><button id='testRpcShouldFail' onClick='testRpcShouldFail(event);'>Test</button></div>
        </div>
    </body>
</html>
